/* registermodel.cpp
 * Class RegisterModel inherits QAbstractTableModel
 * Class RegisterRefreshModel inherits RegisterModel
 * Table models behind the main register table and the refreshed register table
 * Maintains a list of all registers and provides a read/write interface for them
 * Also allows drag and drop of the registers
 * Lots of overloaded functions, read up on the Qt Model/View framework to understand
 */

#include "registermodel.h"

RegisterModel::RegisterModel(QObject *parent) : QAbstractTableModel(parent)
{
    m_regList = 0;
    setSupportedDragActions(Qt::CopyAction);
}

RegisterModel::~RegisterModel()
{
}

void RegisterModel::setRegisterList(QList<RegisterDef> *regList)
{
    m_regList = regList;
}

Qt::ItemFlags RegisterModel::flags(const QModelIndex &index) const
{
    //Items from the main register list table are draggable
    if (!index.isValid())
        return Qt::NoItemFlags;

    Qt::ItemFlags returnFlags = ( Qt::ItemIsEnabled | Qt::ItemIsDragEnabled );

    if (!m_regList->isEmpty() && (index.row() < m_regList->count())) {
        if (m_regList->at(index.row()).readable())
                returnFlags |= ( Qt::ItemIsSelectable );

        //Values are editable if the register is writeable
        if (index.column() == 1) {
            if (m_regList->at(index.row()).writeable())
                returnFlags |= ( Qt::ItemIsEditable );
            }
        return returnFlags;
    }
    return Qt::NoItemFlags;
}

QVariant RegisterModel::data(const QModelIndex &index, int role) const
{
    if ( index.isValid() && role == Qt::DisplayRole && (index.row() < m_regList->count()) ) {
        if (index.column() == 0)
            return m_regList->at(index.row()).name();
        if (index.column() == 1)
            return m_regList->at(index.row()).getValue();
        if (index.column() == 2)
            return m_regList->at(index.row()).address();
        if (index.column() == 3)
            return m_regList->at(index.row()).flags();
    }
    return QVariant();
}

QVariant RegisterModel::headerData(int sections, Qt::Orientation orientation, int role) const
{
    if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
        if ( sections == 0 )
            return "Register Name";
        if ( sections == 1 )
            return "Register Value";
        if ( sections == 2 )
            return "Register Address";
        if ( sections == 3 )
            return "Register Flags";
    }
    return QVariant();
}

int RegisterModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    if (m_regList)
        return m_regList->count();
    else
        return 0;
}

int RegisterModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return 4;
}

bool RegisterModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    //Queue a write of the new value, but DON'T emit the dataChanged() signal until the write has actually succeeded
    if (!value.toString().isEmpty()) {
        if ( index.isValid() && role == Qt::EditRole ) {
            emit writeNeeded(m_regList->at(index.row()).address(), value.toString());
            return true;
        }
    }
    return false;
}

QStringList RegisterModel::mimeTypes() const
{
    //Needed for Drag-n-drop support
    QStringList types;
    types << "application/vnd.text.list";
    return types;
}

 QMimeData *RegisterModel::mimeData(const QModelIndexList &indexes) const
 {
     //Encode the dragged values as a string list of register names
     QMimeData *mimeData = new QMimeData();
     QByteArray encodedData;

     QDataStream stream(&encodedData, QIODevice::WriteOnly);

     foreach (QModelIndex modelIndex, indexes) {
         if (modelIndex.isValid() && modelIndex.column() == 0) {
             QString text = data(modelIndex, Qt::DisplayRole).toString();
             stream << text;
         }
     }

     mimeData->setData("application/vnd.text.list", encodedData);
     return mimeData;
 }

void RegisterModel::dataRefreshed(RegisterDef reg) {
    //A register was updated - let the table know
    int regIndex = m_regList->indexOf(reg);
    QModelIndex modelIndex = index(regIndex, 1);
    emit dataChanged(modelIndex, modelIndex);
}

/* Class RegisterRefreshModel inherits RegisterModel
 * overloads relevant functions to make the model readonly
 * only supports dropping instead of dragging
 */

RegisterRefreshModel::RegisterRefreshModel(QObject *parent) : RegisterModel(parent)
{
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateRegisters()));

    RegisterDef reg = RegisterDef("drop registers here",-1,0);
    m_graphedRegList.append(reg);
}

RegisterRefreshModel::~RegisterRefreshModel()
{
}

Qt::ItemFlags RegisterRefreshModel::flags(const QModelIndex &index) const
{
    //overload to only support dropping not dragging
    if (!index.isValid())
        return Qt::NoItemFlags;

    Qt::ItemFlags returnFlags = ( Qt::ItemIsEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable );

    return returnFlags;
}

QVariant RegisterRefreshModel::data(const QModelIndex &index, int role) const
{
    //return the value from the parent model register list, not the the local one which only contains register names
    if (index.isValid() && role == Qt::DisplayRole) {
        if (m_regList->contains(m_graphedRegList.at(index.row()))) {
            RegisterDef reg = m_regList->at(m_regList->indexOf(m_graphedRegList.at(index.row())));
            if (index.column() == 0)
                return reg.name();
            if (index.column() == 1)
                return reg.getValue();
            if (index.column() == 2)
                return reg.address();
            if (index.column() == 3)
                return reg.flags();
        } else {
            if (index.column() == 0)
                return "drop registers here";
        }
    }
    return QVariant();
}

bool RegisterRefreshModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    //Read-only remember?
    return false;
}

int RegisterRefreshModel::rowCount(const QModelIndex &parent) const
{
    return m_graphedRegList.count();
}

bool RegisterRefreshModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    //Decode the dropped data and add the relevant register names to the local list
    if (action == Qt::IgnoreAction)
         return true;

     if (!data->hasFormat("application/vnd.text.list"))
         return false;

    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;

    while (!stream.atEnd()) {
     QString text;
     stream >> text;
     newItems << text;
    }

    qDebug() << "Decoded dropped data as " << newItems;

    foreach (QString regName, newItems) {
        RegisterDef reg = RegisterDef(regName);
        if (m_regList->contains(reg)) {
            reg = m_regList->at(m_regList->indexOf(reg));
            m_graphedRegList.append(reg);
        }
    }

    reset();

    return true;
}

void RegisterRefreshModel::dataRefreshed(RegisterDef reg)
{
    //Indexes in this table correspond to the local list, not the parent model one
    int regIndex = m_graphedRegList.indexOf(reg);
    QModelIndex modelIndex = index(regIndex, 1);
    emit dataChanged(modelIndex, modelIndex);
}

void RegisterRefreshModel::removeRegisters(QList<QModelIndex> selectedModel)
{
    //the call to reset() resets the TableView and actually causes the rows to disappear
    for (register int i=0; i < selectedModel.count(); i++) {
        m_graphedRegList.removeAt(selectedModel.at(i).row());
    }
    reset();
}

void RegisterRefreshModel::updateRegisters()
{
    //queue a read for each of the registers in the local list
    if (!m_graphedRegList.isEmpty()) {
        foreach (RegisterDef reg, m_graphedRegList) {
            if (m_regList) {
                if (m_regList->contains(reg))
                    emit readNeeded(reg.address());
            }
        }
    }
}

void RegisterRefreshModel::start(int msecs)
{
    //used for changing the update interval
    if (msecs > 0) {
        timer->start(msecs);
    }
}

void RegisterRefreshModel::pause()
{
    timer->stop();
}

